#include "EventLoop.h"
#include "unistd.h"
#include <iostream>
using std::cerr;
using std::cout;
using std::endl;

EventLoop::EventLoop(Acceptor &acceptor)
    : _epfd(createEpollFd()),
      _evtList(1024),
      _acceptor(acceptor),
      _isLooping(false),
      _evtFd(createEvtfd())
{
    // 将listenfd放在红黑树上进行监听（socket）
    int listenfd = _acceptor.getFd();
    addEpollReadFd(listenfd);
    addEpollReadFd(_evtFd);
}

EventLoop::~EventLoop()
{
    close(_epfd);
}

void EventLoop::loop()
{
    _isLooping = true;
    while (_isLooping)
    {
        waitEpollFd();
    }
}

void EventLoop::unloop()
{
    _isLooping = false;
}

void EventLoop::waitEpollFd()
{
    int nready = 0;
    do
    {
        nready = epoll_wait(_epfd, &*_evtList.begin(), _evtList.size(), 3000);
    } while (-1 == nready && errno == EINTR);

    if (-1 == nready)
    {
        cerr << "-1 == nready" << endl;
        return;
    }
    else if (0 == nready)
    {
        cout << ">>epoll_wait timeout" << endl;
    }

    if (nready == (int)_evtList.size())
    {
        _evtList.reserve(2 * nready);
    }

    for (int i = 0; i < nready; ++i)
    {
        int fd = _evtList[i].data.fd;
        // 查看文件描述符是不是listenfd
        if (fd == _acceptor.getFd())
        {
            if (_evtList[i].events & EPOLLIN)
            {
                // 处理新的连接
                handleNewConnection();
            }
        }
        else if (fd == _evtFd)
        {
            readEvtFd();
            doPendingCB();
        }   
        else
        {
            if (_evtList[i].events & EPOLLIN)
            {
                // 处理老的连接
                handleMessage(fd);
            }
        }
    }
}

void EventLoop::handleNewConnection()
{
    int connfd = _acceptor.accept();
    if (connfd < 0)
    {
        perror("handleNewConnection accept");
        return;
    }

    addEpollReadFd(connfd);

    // 就表明三次握手已经建立成功了
    /* TcpConnection con(connfd); */
    TcpConnectionPtr con(new TcpConnection(connfd, *this));
    con->setNewConnectionCB(_onNewConnectionCB);
    con->setMessageCB(_onMessageCB);
    con->setCloseCB(_onCloseCB);

    con->handleNewConnectionCB();
    // 以键值对的形式存起来
    /* _conns.insert(std::make_pair(connfd, con)); */
    _connections[connfd] = con;
}

void EventLoop::handleMessage(int fd)
{
    auto it = _connections.find(fd);
    if (it != _connections.end())
    {
        if (it->second->isClose())
        {
            it->second->handleCloseCB();
            delEpollReadFd(fd);
            _connections.erase(it);
        }
        else
        {
            it->second->handleMessageCB();
        }
        // 连接是存在的，可以进行数据的收发
    }
    else
    {
        // 连接不存在，可以直接让程序退出来
        cout << "连接不存在" << endl;
        return;
    }
}

int EventLoop::createEpollFd()
{
    int fd = epoll_create(7);
    if (fd < 0)
    {
        perror("epoll_create");
        return fd;
    }
    return fd;
}

void EventLoop::addEpollReadFd(int fd)
{
    struct epoll_event evt;
    evt.events = EPOLLIN;
    evt.data.fd = fd;

    int ret = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &evt);
    if (ret < 0)
    {
        perror("epoll_ctl add");
        return;
    }
}

void EventLoop::delEpollReadFd(int fd)
{
    struct epoll_event evt;
    evt.events = EPOLLIN;
    evt.data.fd = fd;

    int ret = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &evt);
    if (ret < 0)
    {
        perror("epoll_ctl add");
        return;
    }
}

void EventLoop::setNewConnectionCB(TcpConnectionCB &&onNewConnectionCB)
{
    _onNewConnectionCB = onNewConnectionCB;
}

void EventLoop::setMessageCB(TcpConnectionCB &&onMessageCB)
{
    _onMessageCB = onMessageCB;
}

void EventLoop::setCloseCB(TcpConnectionCB &&onCloseCB)
{
    _onCloseCB = onCloseCB;
}

void EventLoop::doPendingCB()
{
    vector<EventLoopCB> tem;
    {
        lock_guard<mutex> locker(_mtx);
        tem.swap(_pendingCB);
    }
    for (auto &cb : tem)
    {
        cb();
    }
}

void EventLoop::addPendingCB(EventLoopCB &&cb)
{
    {
        lock_guard<mutex> locker(_mtx);
        _pendingCB.push_back(cb);
    }
    writeEvtFd();
}

int EventLoop::createEvtfd()
{
    int evtfd = eventfd(0, 0);
    return evtfd;
}

void EventLoop::writeEvtFd()
{
    uint64_t z = 1;
    ssize_t ret = write(_evtFd, &z, sizeof(uint64_t));
    if (ret != sizeof(uint64_t))
    {
        perror("writeEvtFd");
        return;
    }
}

void EventLoop::readEvtFd()
{
    uint64_t z;
    ssize_t ret = read(_evtFd, &z, sizeof(uint64_t));
    if (ret != sizeof(uint64_t))
    {
        perror("writeEvtFd");
        return;
    }
}